{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using MMIO with PYNQ\n",
    "\n",
    "## Goal\n",
    "\n",
    "The aim of this notebook is to show how to use the MMIO (Memory Mapped I/O) PYNQ class.   \n",
    "\n",
    "## Hardware design\n",
    "\n",
    "This example uses the same bitstream from the previous example with three AXI GPIO controllers connected to the LEDs, buttons, and switches. While there are PYNQ drivers available to read and write the AXI GPIO LEDs, switches and buttons for demonstration purposes the AXI GPIO controllers will be used to show how the PYNQ MMIO class can be used. \n",
    "\n",
    "![AXI GPIO Design](./images/axi_gpio_design.png \"AXI GPIO Design\")\n",
    "\n",
    "This notebook will seem very similar to the previous lab. We will be exercising the buttons, switches and LEDs in a similar way,  but you should note that we are using the MMIO class directly, and that there are small differences in code. For the MMIO class, we will be specifying an offset address that we read or write to. \n",
    "\n",
    "If you examine the driver code for the *LED*, *switches*, and *buttons* classes, you will notice that they use the PYNQ MMIO class. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Download the axi_gpio.bit overlay"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import Overlay\n",
    "axi_gpio_design = Overlay(\"./bitstream/axi_gpio.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## MMIO class\n",
    "\n",
    "MMIO can map arrays, or a range of addresses. A physical memory address and a range are required by the MMIO class. \n",
    "\n",
    "In this example, the MMIO class will be used to directly access the register space of the AXI GPIO and control the IP. \n",
    "\n",
    "An AXI GPIO controller has two channels. In the design, only 1 channel of each AXI controller is used (as described in the previous lab). We will only use two registers:\n",
    "\n",
    "The data register is mapped to offset 0x0, and the tri-state register is mapped to offset 0x4. To use an AXI GPIO, the tri-state register must be set to configure the IP as input or output. The data register can be read or written to. For example, the AXI GPIO connected to the LEDs sets the tri-state register to configure the IP as an output. The LEDs will turn on or off corresponding to the value written to the data register. For the buttons, or switches, the IP is configured as *input* and the value in the data register will be the value corresponding to the position of the switches or buttons. \n",
    "\n",
    "In the following example, 3 MMIO instances will be created corresponding  to each AXI GPIO. \n",
    "\n",
    "First assign the physical addresses of the controllers to python variables. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "buttons_address = axi_gpio_design.ip_dict['buttons']['phys_addr']\n",
    "switches_address = axi_gpio_design.ip_dict['switches']['phys_addr']\n",
    "leds_address = axi_gpio_design.ip_dict['leds']['phys_addr']\n",
    "\n",
    "print(\"Physical address of buttons:  0x\" + format(buttons_address, '02x'))\n",
    "print(\"Physical address of switches: 0x\" + format(switches_address, '02x'))\n",
    "print(\"Physical address of LEDs:     0x\" + format(leds_address, '02x'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Controlling the switches and push-buttons (again!)\n",
    "\n",
    "An `MMIO` instance is created with an address and optionally a range. The range specifies the range of addresses that can be accessed from the base address. Care must be taken when reading and writing addresses in the system that they physically exist. Reading or writing to location that is not accessible can cause the system to hang.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import MMIO\n",
    "RANGE = 8 # Number of bytes; 8/4 = 2x 32-bit locations which is all we need for this example\n",
    "buttons = MMIO(buttons_address, RANGE) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Write 0xffffffff to the tri-state register at offset 0x4 to configure the IO as inputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "buttons.write(0x4, 0xffffffff) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Push-buttons: {buttons.read()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As before, try pressing any combination of the push-buttons while rerunning the cell above.\n",
    "\n",
    "The AXI GPIO controller for the switches can be used in a similar way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "switches = MMIO(switches_address, RANGE)\n",
    "switches.write(0x4, 0xffffffff) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"Switches: {switches.read()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Controlling the LEDs (again!)\n",
    "\n",
    "The LEDs can be used in a similar way, this time `0x0` is written to the tri-state register to configure the IO as output. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "leds = MMIO(leds_address, RANGE)\n",
    "leds.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "leds.write(0x0, 0xf) # Write 0xf to location 0x0 (Turn on the LEDs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Putting it together (again!)\n",
    "\n",
    "Similarly to the previous lab, we will run a loop to set the LEDs to the value of the pushbuttons.\n",
    "\n",
    "Before executing the next cell, make sure Switch 0 (SW0) is \"on\". While the loop is running, press a push-button and notice the corresponding LED turns on. To exist the loop, change Switch 0 to off."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "while((switches.read(0x0) & 0x1) == 1):\n",
    "    leds.write(0x0, buttons.read(0x0))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
